Skip to content

BREAKING CHANGE: update: migrate to better-sqlite3#20

Merged
deadlyjack merged 15 commits intoAcode-Foundation:mainfrom
UnschooledGamer:remove/sqlite3
Feb 19, 2026
Merged

BREAKING CHANGE: update: migrate to better-sqlite3#20
deadlyjack merged 15 commits intoAcode-Foundation:mainfrom
UnschooledGamer:remove/sqlite3

Conversation

@UnschooledGamer
Copy link
Member

@UnschooledGamer UnschooledGamer commented Feb 8, 2026

Why

sqlite3 has received no updates in the past 2-3 years and has blockers on package updating, like tar/node-tar, which has vulnerabilities.

Better-sqlite3 is benchmarked to be faster, better, and more updated in comparison to sqlite3.


I've tested as much as I could; breaking changes could occur because of how the better-sqlite3 API is without callbacks.

To Do

The update schema script needs changes.

@UnschooledGamer UnschooledGamer changed the title update: migrate to better-sqlite3 BREAKING CHANGE: update: migrate to better-sqlite3 Feb 8, 2026
@UnschooledGamer UnschooledGamer marked this pull request as ready for review February 8, 2026 09:17
@UnschooledGamer UnschooledGamer added the enhancement New feature or request label Feb 8, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 8, 2026

Greptile Summary

This PR migrates the database layer from sqlite3 (callback-based) to better-sqlite3 (synchronous), resolving security vulnerabilities in outdated dependencies and improving performance.

Key Changes:

  • Replaced callback-based db.all() and db.get() with synchronous prepared statements (db.prepare().all() and .get())
  • Write operations (INSERT/UPDATE/DELETE) now return empty arrays instead of result objects for backward compatibility
  • Added directory creation logic in server/lib/db.js to prevent startup failures
  • Fixed server/apis/comment.js to fetch newly inserted comment after insert (since insert no longer returns the row)
  • Added manual cascade deletion in Plugin.deletePermanently() to handle related records (comments, downloads, purchase orders)
  • Fixed type conversions: OTP values stringified, boolean-to-integer for is_default, increment values stringified
  • Improved error handling and null safety across OTP and user APIs
  • Client-side improvements: fixed comment ID handling and added deletion success feedback

Migration Quality:

  • The migration is well-executed with proper synchronous error handling
  • The developer correctly identified and fixed all callback-to-sync conversions
  • Type conversions have been handled appropriately for SQLite's type system
  • Previous review comments about syntax errors and missing closing braces were incorrect - the code structure is valid

Confidence Score: 4/5

  • This PR is generally safe to merge with careful testing of database operations
  • The migration is well-implemented with proper error handling and backward compatibility. However, the breaking changes to the database layer require thorough testing of all CRUD operations, especially edge cases around concurrent inserts and type conversions. The manual cascade deletion logic needs verification that it covers all foreign key relationships.
  • Pay close attention to server/entities/entity.js (core database abstraction), server/entities/plugin.js (manual cascade logic), and server/apis/comment.js (post-insert fetch pattern)

Important Files Changed

Filename Overview
server/lib/db.js Migrated from sqlite3 to better-sqlite3 with synchronous API, added directory creation logic and foreign key enforcement
server/entities/entity.js Replaced callback-based DB calls with synchronous prepared statements, write operations now return empty arrays for compatibility
server/apis/comment.js Fixed comment insertion to fetch the newly created comment ID after insert (previously relied on insert return value)
server/apis/plugin.js Added await to Order.insert, stringified increment value to preserve integer type, added default for minVersionCode
server/apis/user.js Fixed boolean-to-integer conversion for is_default, added OTP validation check, improved null safety for OTP row
server/entities/plugin.js Added manual cascade deletion of related comments, downloads, and purchase orders before plugin deletion
updateSchema.js Migrated to synchronous db.exec() and db.close() with proper error handling for duplicate columns

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[sqlite3 with callbacks] -->|Migration| B[better-sqlite3 synchronous]
    B --> C[Database Layer<br/>server/lib/db.js]
    C --> D{Foreign Keys Enabled}
    D -->|Yes| E[Entity Layer<br/>server/entities/entity.js]
    E --> F[execSql: Prepared Statements]
    F --> G{Statement Type}
    G -->|SELECT| H[stmt.all returns rows]
    G -->|INSERT/UPDATE/DELETE| I[stmt.run returns empty array]
    I --> J[API Layer Adaptations]
    H --> J
    J --> K[Comment API<br/>Fetch after insert]
    J --> L[Plugin API<br/>Manual cascades]
    J --> M[User/OTP APIs<br/>Type conversions]
Loading

Last reviewed commit: 6b9dcad

greptile-apps[bot]

This comment was marked as outdated.

@greptile-apps

This comment was marked as resolved.

@UnschooledGamer

This comment was marked as outdated.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps

This comment has been minimized.

UnschooledGamer and others added 2 commits February 9, 2026 18:38
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@UnschooledGamer
Copy link
Member Author

@greptileai Review it again.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 9, 2026

Additional Comments (1)

server/apis/plugin.js
Missing await on insert

In the paid-download flow, Order.insert(...) is called without await (and without handling the returned Promise). With the new DB layer being synchronous under the hood but still wrapped in Promises, this means the request can proceed (and later logic can observe no order) before the insert completes, and errors from the insert will be unhandled. Make this await Order.insert(...) (or otherwise handle/rethrow failures).

@UnschooledGamer
Copy link
Member Author

@greptileai Review it again.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +360 to 364
// (if needed, in future) `result` could be set as { rows: [], changes: ..., lastInsertRowid: ... }
// for now, we'll just set it as an empty array
stmt.run(...values);
result = [];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write operations (INSERT/UPDATE/DELETE) now return empty array instead of the actual result info (changes, lastInsertRowid). If callers ever need the insert ID or affected row count, they won't have access to it. This could break code that relies on these values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be handled in future if needed, Currently it returns empty array for compatibility

@deadlyjack
Copy link
Member

@greptileai Review changes.

@deadlyjack
Copy link
Member

deadlyjack commented Feb 17, 2026

@greptileai review again

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

15 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 17, 2026

Additional Comments (1)

server/entities/plugin.js
execSql called without required values and entity arguments

Entity.execSql(sql) is called with only one argument. Inside execSql, values is undefined and entity is undefined. When the query runs, stmt.all(...values) will throw TypeError: undefined is not iterable. Additionally, if an error is caught, entity.table will itself throw TypeError: Cannot read properties of undefined, masking the original error and crashing the server.

This will break the earnings calculation jobs in fixUserEarnings.js and updateEarnings.js, both of which call Plugin.getUsersWithPlugin().

    return Entity.execSql(sql, [], this);

Ajit Kumar and others added 2 commits February 19, 2026 10:48
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@UnschooledGamer
Copy link
Member Author

@greptileai Review Once Again.

@deadlyjack deadlyjack merged commit 1cf92c6 into Acode-Foundation:main Feb 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants